package com.rexolar.openapi.job;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.rexolar.openapi.bean.DeviceBasicData;
import com.rexolar.openapi.db.entity.PaasGatewayCredentials;
import com.rexolar.openapi.db.entity.PaasGatewayDeviceRelation;
import com.rexolar.openapi.db.mapper.SaasUserInfoMapper;
import com.rexolar.openapi.enums.GwDeviceStatusEnum;
import com.rexolar.openapi.enums.GwStatusEnum;
import com.rexolar.openapi.exception.TokenException;
import com.rexolar.openapi.service.RexolarApiClient;
import com.rexolar.openapi.service.impl.GatewayCredentialsService;
import com.rexolar.openapi.service.impl.PaasGatewayDeviceRelationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class GwDeviceSyncJob {
    @Autowired
    RexolarApiClient rexolarApiClient;

    @Autowired
    GatewayCredentialsService gatewayCredentialsService;

    @Autowired
    PaasGatewayDeviceRelationService paasGatewayDeviceRelationService;

    @Autowired
    SaasUserInfoMapper saasUserInfoMapper;

    public static final Cache<String, String> userMap = CacheBuilder.newBuilder()
            .expireAfterWrite(9000, TimeUnit.SECONDS)
            .build();

    public static final Cache<String, List<String>> gwRelationMap = CacheBuilder.newBuilder()
            .expireAfterWrite(9000, TimeUnit.SECONDS)
            .build();

    /**
     */
    @Scheduled(fixedRate = 60 * 1000)
    public void syncGwStatus() {
        long startTime = System.currentTimeMillis();
        log.info("syncGwStatus start, time:{}", startTime);
        LambdaQueryWrapper<PaasGatewayCredentials> queryWrapper = new LambdaQueryWrapper<PaasGatewayCredentials>();
        queryWrapper.eq(PaasGatewayCredentials::getStatus, GwStatusEnum.CREATED.getCode());
        List<PaasGatewayCredentials> gwList = gatewayCredentialsService.list(queryWrapper);
        if(CollectionUtils.isEmpty(gwList)){
            log.info("syncGwStatus no gw to sync");
            return;
        }

        for (PaasGatewayCredentials gw : gwList) {
            String tenantId = gw.getTenantId();
            String token = getToken(tenantId);
            if(StringUtils.isEmpty(token)){
//                log.error("syncGwStatus getToken error, tenantId:{}", tenantId);
                continue;
            }

            String gwId = gw.getGwId();
            try {
                Map<String,Object> attributes = rexolarApiClient.getDeviceAttributes(gwId, token, new String[]{"lastActivityTime"});
                if(CollectionUtils.isEmpty(attributes)){
                    log.error("syncGwStatus getDeviceAttributes error, gwId:{}", gwId);
                    continue;
                }
                gw.setStatus(GwStatusEnum.ON.getCode());
                gw.setGmtModified(new Date());
                if(!gatewayCredentialsService.updateById(gw)){
                    log.error("syncGwStatus update gatewayCredentials error, gw:{}", gw);
                }
            }catch (TokenException e){
                userMap.invalidate(tenantId);
            }
        }
        log.info("syncGwStatus end, time:{}", System.currentTimeMillis()-startTime);
    }

    private String getToken(String tenantId) {
        if(userMap.getIfPresent(tenantId)!=null){
            return userMap.getIfPresent(tenantId);
        }else {
            String token = rexolarApiClient.getTenantToken(tenantId);
            if(StringUtils.isEmpty(token)){
//                log.error("getToken getToken error, tenantId:{}", tenantId);
                userMap.put(tenantId, "");
                return null;
            }
            userMap.put(tenantId, token);
            return token;
        }
    }


    /**
     */
    @Scheduled(fixedRate = 60 * 1000)
    public void syncGwDeviceRelation() {
        long startTime = System.currentTimeMillis();
        log.info("syncGwDeviceRelation start, time:{}", startTime);
        LambdaQueryWrapper<PaasGatewayCredentials> queryWrapper = new LambdaQueryWrapper<PaasGatewayCredentials>();
        queryWrapper.eq(PaasGatewayCredentials::getStatus, GwStatusEnum.ON.getCode());
        List<PaasGatewayCredentials> gwList = gatewayCredentialsService.list(queryWrapper);
        if(CollectionUtils.isEmpty(gwList)){
            return;
        }
        for (PaasGatewayCredentials gw : gwList) {
            String gwId = gw.getGwId();
            String tenantId = gw.getTenantId();
            String token = getToken(tenantId);
            if(StringUtils.isEmpty(token)){
//                log.error("syncGwDeviceRelation getToken error, tenantId:{}", tenantId);
                continue;
            }
            try {
                List<DeviceBasicData> deviceList = rexolarApiClient.getRelationsInfo(token, gwId);
                List<PaasGatewayDeviceRelation> relations = new ArrayList<>();
                if(!CollectionUtils.isEmpty(deviceList)){
                    for (DeviceBasicData device : deviceList) {
                        PaasGatewayDeviceRelation relation = new PaasGatewayDeviceRelation();
                        relation.setDeviceId(device.getCoreId());
                        relation.setDeviceName(device.getName());
                        relation.setGwId(gwId);
                        relation.setStatus(GwDeviceStatusEnum.ON.getCode());
                        relation.setTenantId(gw.getTenantId());
                        relation.setGmtCreate(new Date());
                        relation.setGmtModified(new Date());
                        relations.add(relation);
                    }

                }
                LambdaQueryWrapper<PaasGatewayDeviceRelation> relationWrapper = new LambdaQueryWrapper<>();
                relationWrapper.eq(PaasGatewayDeviceRelation::getGwId, gwId);
                paasGatewayDeviceRelationService.remove(relationWrapper);
                if(!CollectionUtils.isEmpty(relations)){
                    if(!paasGatewayDeviceRelationService.saveBatch(relations)){
                        log.error("syncGwDeviceRelation save relation failed, gwId:{}", gwId);
                    }
                }
            }catch (TokenException e){
                userMap.invalidate(tenantId);
            }
        }
        log.info("syncGwDeviceRelation end, time:{}", System.currentTimeMillis()-startTime);
    }
}
